import { defineComponent, reactive, watch, computed } from 'vue';
import getTool from '../tools/index';
import MyInput from './input';
import event from '../tools/event';
import fnModules from '../tools/fns';
import Toast from './toast';
const tools = getTool();

const mutilTable = defineComponent({
  name: 'mutilTable',
  props: {
    tableData: {
      type: Array,
      default: () => [],
    },
    tableHeader: {
      type: Array,
      default: () => [],
    },
    bodyNotShowProps: {
      type: Array,
      default: () => [],
    },
    tableBorderColor: {
      type: String,
      default: '#ddd',
    },
    cellHeight: {
      type: Number,
      default: 40,
    },
    uniqueKey: {
      type: String,
      default: '',
    },
    firstThClickHandler: {
      type: Function,
      default: () => null,
    },
    isFirstThEableClick: {
      type: Boolean,
      default: false,
    },
    firstThStyle: {
      type: Object,
      default: () => ({}),
    },
    isReadOnly: {
      type: Boolean,
      default: false,
    },
    bodyEmptyTips: {
      type: String,
      default: '暂无数据',
    },
    headerStyle: {
      type: Object,
      default: () => ({
        background: '#f5f7fa',
        color: '#333',
      }),
    },
    cellStyle: {
      type: Object,
      default: () => ({
        background: '#fff',
        color: '#333',
      }),
    },
    calcCellStyle: {
      type: Object,
      default: () => ({
        background: '#999',
        color: '#fff',
      }),
    },
  },
  setup(props) {
    const state = reactive({
      ossTableHeader: [],
      ossTableData: [],
      headerArr: [],
      bodyNotShowPropData: ['cell_id'],
      curTableData: [],
      curEditTdId: '',
      isBodyEmpty: false,
    });

    const initData = () => {
      state.headerArr = [];

      state.ossTableData = tools.deepCopy(props.tableData);
      const ossTableHeader = tools.deepCopy(props.tableHeader);
      state.curTableData = tools.deepCopy(props.tableData);
      state.bodyNotShowPropData = [
        ...new Set([...tools.deepCopy(props.bodyNotShowProps), 'table_id']),
      ];

      state.ossTableData.forEach((item) => {
        if (!props.uniqueKey) {
          if (!item.table_id) {
            item.table_id = tools.guid();
          }
        } else {
          item.table_id = item[props.uniqueKey] ? item[props.uniqueKey] : tools.guid();
        }
      });
      if (state.ossTableData.length) {
        for (const [k, v] of Object.entries(state.ossTableData[0])) {
          if (typeof v === 'object') {
            combineCellByKey(k);
          }
        }
        state.isBodyEmpty = false;
      } else {
        state.isBodyEmpty = true;
      }

      state.ossTableHeader = giveIdx2Item(ossTableHeader);
      getHeaderItemArr(state.ossTableHeader);
    };

    const combineCellByKey = (key) => {
      const arr = [];
      state.ossTableData.forEach((item) => {
        const _idx = arr.findIndex((_) => tools.checkIfObjectEqual(_, item[key]));
        if (_idx === -1) {
          arr.push(item[key]);
        } else {
          delete item[key];
        }
      });
    };

    const classifyHeaderHandler = () => {
      const common = {
        verticalAlign: 'middle',
        borderRight: `1px solid ${props.tableBorderColor}`,
      };
      return (
        <thead>
          {headerClasses.value.map((val) => {
            return (
              <tr
                style={{
                  ...props.headerStyle,
                  borderTop: `1px solid ${props.tableBorderColor}`,
                }}
              >
                {state.headerArr
                  .filter((item) => item.classifyId === val)
                  .filter((item) => item.title)
                  .map((item, _idx) => {
                    return (
                      <th
                        rowspan={getRowspan(item)}
                        colspan={item.colSpan ? item.colSpan : 1}
                        style={
                          _idx === 0 &&
                          props.isFirstThEableClick &&
                          item.sortIdx === '0' &&
                          item.classifyId === 0
                            ? {
                                ...common,
                                ...props.firstThStyle,
                                cursor: 'pointer',
                                height: `${props.cellHeight * getRowspan(item)}px`,
                              }
                            : {
                                ...common,
                                height: `${props.cellHeight * getRowspan(item)}px`,
                              }
                        }
                        onClick={() => {
                          if (
                            !props.isFirstThEableClick ||
                            _idx !== 0 ||
                            item.sortIdx !== '0' ||
                            item.classifyId !== 0
                          ) {
                            return;
                          }
                          props.firstThClickHandler();
                        }}
                      >
                        <span>{item.title}</span>
                      </th>
                    );
                  })}
              </tr>
            );
          })}
        </thead>
      );
    };

    const tdClickHandler = (tableId, isCanEdit) => {
      if (!isCanEdit) {
        return;
      }

      state.curEditTdId = tableId;
    };

    const renderPanelBody = () => {
      const emptyBody = () => {
        return (
          <div
            class="flexBox"
            style={{
              height: '100px',
              border: `1px solid ${props.tableBorderColor}`,
            }}
          >
            {props.bodyEmptyTips}
          </div>
        );
      };

      const renderBody = () => {
        return (
          <tbody
            style={{
              width: '100%',
              borderTop: `1px solid ${props.tableBorderColor}`,
            }}
          >
            {state.ossTableData.map((item) => renderTableColumn(item))}
          </tbody>
        );
      };

      return (
        <div>
          <table
            cellspacing="0"
            cellpadding="0"
            style={{
              width: '100%',
              borderLeft: `1px solid ${props.tableBorderColor}`,
              borderRight: `1px solid ${props.tableBorderColor}`,
            }}
          >
            {classifyHeaderHandler()}
            {state.isBodyEmpty === false ? renderBody() : null}
          </table>
          {state.isBodyEmpty === false ? null : emptyBody()}
        </div>
      );
    };

    const getHeaderItemSortIndex = (target_key) => {
      const _idx = state.headerArr.findIndex((item) => item.key === target_key);
      if (_idx === -1) {
        return '';
      }
      return state.headerArr[_idx].sortIdx;
    };

    const getHeaderItemArr = (arr1) => {
      const bianli = (arr) => {
        arr.forEach((item) => {
          if (item.children && item.children.length) {
            const idx = state.headerArr.findIndex(
              (_) => _.title === item.title && _.sortIdx === item.sortIdx,
            );
            if (idx === -1) {
              const _temp = tools.deepCopy(item);
              delete _temp.children;
              state.headerArr.push(_temp);
            }
            bianli(Reflect.get(item, 'children'));
          } else {
            const _idx = state.headerArr.findIndex((_) => _.key === item.key);
            if (_idx === -1) {
              state.headerArr.push(item);
            }
          }
        });
      };
      bianli(arr1);
    };

    const getRowspan = (cell) => {
      return cell.rowSpan ? cell.rowSpan : 1;
    };

    const getIfHeaderItemCanEditByKey = (key) => {
      const _idx = state.headerArr.findIndex((item) => item.key === key);
      if (_idx === -1) {
        return false;
      }
      return state.headerArr[_idx].isCanEdit;
    };

    const isAfrontB = (A, B) => {
      if (!A || !B) {
        return 0;
      }
      const A_arr = A.split('_').map((item) => Number(item));
      const B_arr = B.split('_').map((item) => Number(item));
      const len = Math.min(...[A_arr.length, B_arr.length]);
      for (let i = 0; i < len; i++) {
        if (A_arr[i] - B_arr[i] > 0) {
          return 1;
        } else if (A_arr[i] - B_arr[i] < 0) {
          return -1;
        }
      }
      return 0;
    };

    const renderTableColumn = (colOptions) => {
      const sortArr = Object.keys(colOptions)
        .filter((item) => !state.bodyNotShowPropData.includes(item))
        .sort((a, b) => {
          const flag = isAfrontB(getHeaderItemSortIndex(a), getHeaderItemSortIndex(b));
          return flag;
        });

      return (
        <tr style={{ ...props.cellStyle, width: '100%' }}>
          {sortArr.map((item, idx) => {
            const common = {
              padding: '0 25px',
              minWidth: '100px',
              height: `${
                props.cellHeight *
                (typeof colOptions[item] === 'object' ? colOptions[item].rowSpan : 1)
              }px`,
            };

            const isReadOnlySpan = (
              <MyInput
                addStyle={
                  colOptions[item].fn
                    ? {
                        ...common,
                        ...props.calcCellStyle,
                      }
                    : {
                        ...common,
                      }
                }
                parentColumnId={
                  props.uniqueKey
                    ? colOptions[props.uniqueKey]
                      ? colOptions[props.uniqueKey]
                      : colOptions['table_id']
                    : colOptions['table_id']
                }
                componentName={defineComponent.name}
                editPropName={item}
                isReadonly
                value={
                  typeof colOptions[item] === 'object' ? colOptions[item].value : colOptions[item]
                }
              />
            );

            const editInput = (
              <MyInput
                style={{ minWidth: '100px' }}
                value={
                  typeof colOptions[item] === 'object' ? colOptions[item].value : colOptions[item]
                }
                parentColumnId={
                  props.uniqueKey
                    ? colOptions[props.uniqueKey]
                      ? colOptions[props.uniqueKey]
                      : colOptions['table_id']
                    : colOptions['table_id']
                }
                addStyle={
                  `td_id_${colOptions[props.uniqueKey]}_${item}_${idx}` !== state.curEditTdId
                    ? {
                        ...props.cellStyle,
                        borderTop: 'none',
                        borderBottom: 'none',
                        borderRight: 'none',
                        textAlign: 'center',
                        borderRadius: 0,
                      }
                    : {
                        textAlign: 'center',
                      }
                }
                editPropName={item}
                componentName={defineComponent.name}
              />
            );

            return state.bodyNotShowPropData.includes(item) ? null : (
              <td
                id={`td_id_${colOptions[props.uniqueKey]}_${item}_${idx}`}
                style={{
                  borderBottom: `1px solid ${props.tableBorderColor}`,
                  verticalAlign: 'middle',
                }}
                colspan="1"
                rowspan={typeof colOptions[item] === 'object' ? colOptions[item].rowSpan : 1}
                onClick={() =>
                  tdClickHandler(
                    `td_id_${colOptions[props.uniqueKey]}_${item}_${idx}`,
                    getIfHeaderItemCanEditByKey(item),
                  )
                }
              >
                {props.isReadOnly
                  ? isReadOnlySpan
                  : getIfHeaderItemCanEditByKey(item)
                  ? typeof colOptions[item] === 'object' && !colOptions[item].isCanEdit
                    ? isReadOnlySpan
                    : editInput
                  : isReadOnlySpan}
              </td>
            );
          })}
        </tr>
      );
    };

    const giveIdx2Item = (arr1, parentSortId = '', classifyId = 0) => {
      return arr1.map((v, idx) => {
        const item = {
          ...tools.deepCopy(v),
          sortIdx: '',
          classifyId: -1,
        };
        if (!item.sortIdx) {
          item.sortIdx = (parentSortId ? parentSortId + '_' : '') + idx;
        }
        item.classifyId = classifyId;
        if (item.children && item.children.length) {
          item.children = giveIdx2Item(Reflect.get(v, 'children'), item.sortIdx, classifyId + 1);
        }
        return item;
      });
    };

    watch(
      () => props.tableHeader,
      (val) => {
        initData();
      },
      { deep: true },
    );

    const headerClasses = computed(() => {
      const arr = state.headerArr.map((item) => item.classifyId);
      return [...new Set(arr)];
    });

    const reCalculate = () => {
      state.ossTableData.forEach((item) => {
        Object.keys(item).forEach((_key) => {
          if (item[_key] - 0) {
            const data = {
              value: Object.is(Number(item[_key]), NaN)
                ? item[_key] === ''
                  ? 0
                  : item[_key]
                : item[_key] - 0,
              prop: _key,
              parentColumnId: item[props.uniqueKey],
            };
            tools.throttle(() => numberChangeHandler(data), 1000);
          }
        });
      });
    };

    const numberChangeHandler = (val) => {
      if (props.isReadOnly) {
        return;
      }
      if (Object.is(Number(val.value), NaN)) {
        event.emit('show-toast', { text: '请输入数字' });
        return;
      }
      const _check = (obj) => {
        for (const [k, v] of Object.entries(obj)) {
          if (val.prop === k) {
            return true;
          }

          if (typeof v === 'object') {
            const flag =
              v.fnParms &&
              v.fnParms.some((item) => {
                return item[props.uniqueKey] === val.parentColumnId && item.key === val.prop;
              });

            if (flag) {
              return true;
            }
          }
        }
        return false;
      };

      const target_arr = state.curTableData.filter(
        (item) => item[props.uniqueKey] === val.parentColumnId || _check(item),
      );
      const _idx = state.curTableData.findIndex(
        (item) => item[props.uniqueKey] === val.parentColumnId,
      );
      const _temp = state.curTableData.find((item) => item[props.uniqueKey] === val.parentColumnId);
      try {
        if (!_temp) {
          return;
        }
        if (_temp[val.prop] && typeof _temp[val.prop] === 'object') {
          _temp[val.prop] = Object.assign({}, _temp[val.prop], {
            value: val.value,
          });
        } else {
          _temp[val.prop] = val.value;
        }
        if (_temp) {
          state.curTableData[_idx] = _temp;
          emit('TableDataChange', state.curTableData);
        }
      } catch (e) {
        throw e;
      }

      const _temp_oss = state.ossTableData.find(
        (item) => item[props.uniqueKey] === val.parentColumnId,
      );
      if (_temp_oss && _temp_oss[val.prop]) {
        if (typeof _temp_oss[val.prop] === 'object') {
          const _copy = tools.deepCopy(_temp_oss[val.prop]);
          if (_copy.value !== val.value) {
            _temp_oss[val.prop] = Object.assign({}, _temp_oss[val.prop], {
              value: val.value,
            });
          }
        } else {
          _temp_oss[val.prop] = val.value;
        }
      }

      const _idx_oss = state.ossTableData.findIndex(
        (item) => item[props.uniqueKey] === val.parentColumnId,
      );
      if (target_arr.length) {
        try {
          target_arr.forEach((item) => {
            for (const [k, v] of Object.entries(item)) {
              if (typeof v === 'object') {
                if (v.fn && v.fnParms && v.fnParms.length) {
                  if (v.fnParms.some((_val) => _val.key === val.prop)) {
                    const argsArr = v.fnParms.map((_val) => {
                      return getValueFromColumn(_val.code, _val.key);
                    });
                    let f = v.fn;
                    const { floatAdd, floatMul, floatDiv } = tools;
                    if (fnModules[f]) {
                      f = fnModules[f];
                    }
                    try {
                      let res = eval('(' + f + `)(${argsArr.toString()})`);
                      if (['false', false].includes(res)) {
                        event.emit('show-toast', {
                          text: '公式运算错误,请检查',
                        });
                        return;
                      }
                      if (!Object.is(Number(res), NaN)) {
                        res = Number(res).toFixed(3);
                      }
                      const flag = ['NaN%', 'Infinity%', 'NaN', 'Infinity'].includes(res);
                      const _copy__ = tools.deepCopy(v);
                      if (flag) {
                        v.value = '';
                      } else {
                        if (_copy__.value - 0 !== res - 0) {
                          v.value = res;
                        }
                      }
                    } catch (error) {
                      throw error;
                    }
                  }
                }
              }
            }
          });
        } catch (err) {
          throw err;
        }
        const copyCurTD = tools.deepCopy(state.curTableData);
        const copyOssTD = tools.deepCopy(state.ossTableData);
        target_arr.forEach((item) => {
          copyCurTD.forEach((_val, _index) => {
            if (item[props.uniqueKey] === _val.code) {
              state.curTableData[_index] = item;
              const c_index = copyOssTD.findIndex((_item) => _item[props.uniqueKey] === _val.code);
              state.ossTableData[c_index] = Object.assign({}, state.ossTableData[c_index], item);
            }
          });
        });
      }
    };

    const getValueFromColumn = (code, key) => {
      const _target = state.curTableData.find((item) => item[props.uniqueKey] === code);
      if (_target) {
        if (typeof _target[key] === 'object') {
          return _target[key].value ? _target[key].value : 0;
        } else {
          return _target[key] ? _target[key] : 0;
        }
      } else {
        return 0;
      }
    };

    const checkIfNum = (n) => {
      return !Object.is(Number(n), NaN) ? Number(n) : 0;
    };

    initData();

    watch(
      () => props.tableHeader,
      () => {
        initData();
      },
      { deep: true },
    );

    event.on(`inputChange-${defineComponent.name}`, (val) => {
      tools.throttle(() => numberChangeHandler(val), 1000);
    });

    return {
      headerClasses,
      reCalculate,
      checkIfNum,
      renderPanelBody,
      state,
    };
  },
  render() {
    return (
      <section class="nui-scroll nui-scroll-x">
        <Toast />
        {this.renderPanelBody()}
      </section>
    );
  },
});

export default mutilTable;
